package org.jpush.core;

import java.io.IOException;
import java.net.InetSocketAddress;

import org.jpush.core.socket.JPushClientSocket;
import org.jpush.core.socket.JPushServerSocket;

public class Listener
{
	// To handle the incoming request
	private AbstractAcceptor acceptor;
	
	// The socket to listen the port
	private JPushServerSocket serverSocket;

	 // If listener is running.
	private volatile boolean blnRunning;
	
	private Thread acceptorThread;

	public Listener(AbstractAcceptor acceptor)
	{
	    this.acceptor = acceptor;
	}
	
	private boolean doListening(InetSocketAddress serverAddr)
	{
		serverSocket = JPushComponentFactory.getServerSocket(
				acceptor.getServer().getServerSocketOption());
		try
		{
			// Open
			serverSocket.open();
			
			// Bind
			serverSocket.bind(serverAddr);
			
			// Accept
			doAccept();

		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
		finally
		{
			if (serverSocket != null)
			{
				serverSocket.close();
			}
		}
		
		return true;
	}
	
	private void doAccept()
	{
		// Start a new thread
		acceptorThread = new Thread(new Runnable() 
		{
			public void run()
			{
				// TODO Auto-generated method stub
				while (blnRunning)
				{
					try
					{
						JPushClientSocket clientSocket = serverSocket.accept();
						// Start servicing the client connection
						if (!acceptor.handleAcceptedSocket(clientSocket))
						{
							clientSocket.close();
						}
					}
					catch (IOException e)
					{
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
			
		});
		
		// Start the thread
		acceptorThread.start();
	}

	public boolean startListening()
	{
		// If already running
		if (blnRunning)
		{
			return true;
		}

		InetSocketAddress serverAddr = null;
		if(acceptor.options.getInterfaceAddress() != null)
	    {
			if (!"255.255.255.255".equals(acceptor.options.getInterfaceAddress()))
			{
				return false;
			}

			serverAddr = new InetSocketAddress(acceptor.options.getInterfaceAddress(), 
					acceptor.getListeningPort());
	    }
	    else
	    {
			serverAddr = new InetSocketAddress(acceptor.getListeningPort());
	    }

	    blnRunning = doListening(serverAddr);
	 
	    return blnRunning;
	}
	
//	public boolean doListening(InetSocketAddress serverAddr)
//	{		
//		// open server socket channel
//		AsynchronousServerSocketChannel serverSocketChannel = null;
//
//        try
//        {
//			serverSocketChannel = AsynchronousServerSocketChannel.open();
//	        // Set server socket channel
//			serverSocketChannel.setOption(StandardSocketOptions.SO_REUSEADDR, 
//					GlobalOptions.REUSE_ADDRESS);  
//			serverSocketChannel.setOption(StandardSocketOptions.SO_RCVBUF, 
//					GlobalOptions.RECV_BUF_SIZE);
//
//	        // bind
//	        serverSocketChannel.bind(serverAddr);
//	        
//	        // Set handler for accept
//	        serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>()
//    		{
//
//				@Override
//				public void completed(AsynchronousSocketChannel result,
//						Object attachment)
//				{
//					// TODO Auto-generated method stub
//					acceptor.handleAcceptedSocket(result);
//				}
//
//				@Override
//				public void failed(Throwable exc, Object attachment)
//				{
//					// TODO Auto-generated method stub
//					
//				}
//    	
//    		}
//	        );
//
//		}
//        catch (IOException e)
//        {
//			e.printStackTrace();
//			return false;
//		}
//        finally
//        {
//    		Utilities.closeAsynchronousServerSocketChannel(serverSocketChannel);
//        }
//	}

	public void stopListening()
	{
		blnRunning = false;

	    // Close server socket
		if (serverSocket != null)
		{
			serverSocket.close();
			serverSocket = null;
		}
		
		// wait the thread to terminate
		if (acceptorThread != null)
		{
			try
			{
				acceptorThread.join();
			}
			catch (InterruptedException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			acceptorThread = null;
		}
	}
}
